using System;
using System.ComponentModel;
using Microsoft.WowAddonStudio.FrameXml.Components.Descriptors.Filters;
using Microsoft.WowAddonStudio.FrameXml.Serialization;

namespace Microsoft.WowAddonStudio.FrameXml.Components.Descriptors
{
    public class FrameXmlCustomTypeDescriptor<T> : CustomTypeDescriptor where T : class
    {
        private readonly T instance;
        private readonly string propertyName;

        private static readonly PropertyDescriptorFilterManager serializationObjectPropertyDescriptorFilterManager = new PropertyDescriptorFilterManager();

        /// <summary>
        /// Initializes the <see cref="FrameXmlCustomTypeDescriptor&lt;T&gt;"/> class.
        /// </summary>
        static FrameXmlCustomTypeDescriptor()
        {
            // Initialize property descriptor filter managers
            serializationObjectPropertyDescriptorFilterManager.Add<UiTypeEditorPropertyDescriptorFilter>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FrameXmlCustomTypeDescriptor{T}"/> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="instance">The inheritable object.</param>
        /// <param name="propertyName"></param>
        public FrameXmlCustomTypeDescriptor(ICustomTypeDescriptor parent, T instance, string propertyName)
            : base(parent)
        {
            this.instance = instance;
            this.propertyName = propertyName;
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="FrameXmlCustomTypeDescriptor&lt;T&gt;"/> class.
        /// </summary>
        /// <param name="parent">The parent.</param>
        /// <param name="inheritableObject">The inheritable object.</param>
        public FrameXmlCustomTypeDescriptor(ICustomTypeDescriptor parent, T inheritableObject)
            : this(parent, inheritableObject, null)
        {
        }

        /// <summary>
        /// Returns the class name of this instance of a component.
        /// </summary>
        /// <returns>
        /// The class name of the object, or null if the class does not have a name.
        /// </returns>
        public override string GetClassName()
        {
            return TypeDescriptor.GetClassName(this, true);
        }

        /// <summary>
        /// Returns the name of this instance of a component.
        /// </summary>
        /// <returns>
        /// The name of the object, or null if the object does not have a name.
        /// </returns>
        public override string GetComponentName()
        {
            if (!Equals(instance, default(T)) && !string.IsNullOrEmpty(((INamedObject)instance).Name))
                return ((INamedObject)instance).Name;

            return TypeDescriptor.GetClassName(this, true);
        }

        /// <summary>
        /// Returns a collection of property descriptors for the object represented by this type descriptor.
        /// </summary>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> containing the property descriptions for the object represented by this type descriptor. The default is <see cref="F:System.ComponentModel.PropertyDescriptorCollection.Empty"/>.
        /// </returns>
        public override PropertyDescriptorCollection GetProperties()
        {
            return GetProperties(null);
        }
        
        /// <summary>
        /// Returns a filtered collection of property descriptors for the object represented by this type descriptor.
        /// </summary>
        /// <param name="attributes">An array of attributes to use as a filter. This can be null.</param>
        /// <returns>
        /// A <see cref="T:System.ComponentModel.PropertyDescriptorCollection"/> containing the property descriptions for the object represented by this type descriptor. The default is <see cref="F:System.ComponentModel.PropertyDescriptorCollection.Empty"/>.
        /// </returns>
        public override PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            // Create a new collection of property descriptors from the serialization objects' property descriptors
            var result = new PropertyDescriptorCollection(null);

            var propertyDescriptorCollection = base.GetProperties(attributes);
            foreach (PropertyDescriptor propertyDescriptor in propertyDescriptorCollection)
            {
                var filteredPropertyDescriptor = serializationObjectPropertyDescriptorFilterManager.Filter(propertyDescriptor.PropertyType, propertyDescriptor);
                if (filteredPropertyDescriptor != null)
                    result.Add(FrameXmlDescriptorFactory.CreateProperty(instance, filteredPropertyDescriptor, propertyName));
            }

            return result;
        }
    }
}